package com.wangzhen.codebase;

import java.util.concurrent.atomic.AtomicLong;

/**
 * java并发问题的源头-》可见性、原子性、有序性
 * 之可见性问题的demo
 * 演示了普通变量：      private long count = 0;
 * 演示了volatile变量： private volatile long count = 0;
 * 演示了AtomicLong：  private AtomicLong count3 = new AtomicLong(0);
 * 这三种类型add10亿的场景，以及可以看到相应的性能变化；
 */
public class VisibilityDemo {
    private long count = 0;
    // 如果加入一个volatile修饰符，那么会发现最终的count值是会大于不加volatile的情况
    // 同时多次运行之后，会发现不加volatile的时候执行时间大约在90ms左右，加上volatile之后执行时间增长到4000ms左右
    // 说明的情况：①volatile要求读写的时候都强制从内存中加载count变量，是有很大的性能消耗的，大概多了四五十倍；
    //           ②从count的值来看，使用volatile修饰变量之后count的值大于不使用volatile修饰的变量修饰的值，
    // 证明使用volatile每次从内存加载之后，有点效果。但是volatile不能解决原子性的问题，所以最终的结果还是有问题的。

    //private volatile long count = 0;

    private AtomicLong count3 = new AtomicLong(0);

    public synchronized void increment3() {
        for (int i = 0; i < 100_000_000; i++) {
            count3.incrementAndGet();
        }
    }

    public synchronized void increment2() {
        for (int i = 0; i < 100_000_000; i++) {
            count ++;
        }
    }

    public void increment(){
        for (int i = 0; i < 100_000_000; i++) {
            count ++;
        }
    }

    /**
     * 在单cpu的场景下，一个线程A修改了cpu缓存的值之后，另外一个线程B去执行的时候是可以能够看到
     * 线程A修改之后的值；
     * 但是在多cpu的场景下，比如下面这个示例在多cpu的场景下，两个线程分别在两个cpu上执行，分别读取
     * count的起始值到各自的【cpu缓存】里面去计算，但是没有看到别的线程对同一个变量的修改，导致最后的结果出错
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();
        VisibilityDemo demo = new VisibilityDemo();
        Thread t1 = new Thread(()-> {demo.increment3();});
        Thread t2 = new Thread(()-> {demo.increment3();});

        t1.start();
        t2.start();

        t1.join();
        t2.join();
        long end = System.currentTimeMillis();
        System.out.println("执行时间="+(end-start)+ "毫秒，结果："+demo.count3.get());
    }
}
